﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2010
// admin@franknagl.de
//
namespace SBIP
{
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    /// <summary>Quad object with position and texture coordinate for each vertex.</summary>
    public class Quad
    {
        /// <summary>The origin vector.</summary>
        public Vector3 Origin;
        /// <summary>The upper-left vector.</summary>
        public Vector3 UpperLeft;
        /// <summary>The lower-left vector.</summary>
        public Vector3 LowerLeft;
        /// <summary>The upper-right vector.</summary>
        public Vector3 UpperRight;
        /// <summary>The lower-right vector.</summary>
        public Vector3 LowerRight;
        /// <summary>The normal vector.</summary>
        public Vector3 Normal;
        /// <summary>The up vector.</summary>
        public Vector3 Up;
        /// <summary>The left vector.</summary>
        public Vector3 Left;
        /// <summary>Array of the quad's vertices</summary>
        public VertexPositionNormalTexture[] Vertices;
        /// <summary>Array of the index buffer for each vertex, using clockwise winding.</summary>
        public int[] Indexes;

        /// <summary>Gets the vertex declaration.</summary>
        public static VertexDeclaration VertexDeclaration { get; protected set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Quad"/> class.
        /// </summary>
        /// <param name="width">The width of the quad.</param>
        /// <param name="height">The height of the quad.</param>
        /// <param name="graphics">The XNA graphics device.</param>
        public Quad(float width, float height, GraphicsDevice graphics) :
            this(Vector3.Zero, Vector3.Backward, Vector3.Up, width, height, graphics)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Quad"/> class.
        /// </summary>
        /// <param name="origin">The origin vector of the quad.</param>
        /// <param name="normal">The normal vector of the quad.</param>
        /// <param name="up">The Up vector of the quad.</param>
        /// <param name="width">The width of the quad.</param>
        /// <param name="height">The height of the quad.</param>
        /// <param name="graphics">The XNA graphics device.</param>
        public Quad(Vector3 origin, Vector3 normal, Vector3 up, float width, float height, GraphicsDevice graphics)
        {
            Vertices = new VertexPositionNormalTexture[4];
            Indexes = new int[6];
            Origin = origin;
            Normal = normal;
            Up = up;

            // Calculate the quad corners
            Left = Vector3.Cross(normal, Up);
            Vector3 uppercenter = (Up * height / 2) + origin;
            UpperLeft = uppercenter + (Left * width / 2);
            UpperRight = uppercenter - (Left * width / 2);
            LowerLeft = UpperLeft - (Up * height);
            LowerRight = UpperRight - (Up * height);
            FillVertices();

            InitVertexDeclaration(graphics);
        }

        /// <summary>
        /// Initializes the static member <c>VertexDeclaration</c>.
        /// </summary>
        /// <param name="graphics">The XNA graphics device.</param>
        private static void InitVertexDeclaration(GraphicsDevice graphics)
        {
            VertexDeclaration = new VertexDeclaration(graphics, VertexPositionNormalTexture.VertexElements);
            //graphics.VertexDeclaration = VertexDeclaration;
        }

        private void FillVertices()
        {
            // Fill in texture coordinates to display full texture
            // on quad
            Vector2 textureUpperLeft = new Vector2(0.0f, 0.0f);
            Vector2 textureUpperRight = new Vector2(1.0f, 0.0f);
            Vector2 textureLowerLeft = new Vector2(0.0f, 1.0f);
            Vector2 textureLowerRight = new Vector2(1.0f, 1.0f);

            // Provide a normal for each vertex
            for (int i = 0; i < Vertices.Length; i++)
            {
                Vertices[i].Normal = Normal;
            }

            // Set the position and texture coordinate for each
            // vertex
            Vertices[0].Position = LowerLeft;
            Vertices[0].TextureCoordinate = textureLowerLeft;
            Vertices[1].Position = UpperLeft;
            Vertices[1].TextureCoordinate = textureUpperLeft;
            Vertices[2].Position = LowerRight;
            Vertices[2].TextureCoordinate = textureLowerRight;
            Vertices[3].Position = UpperRight;
            Vertices[3].TextureCoordinate = textureUpperRight;

            // Set the index buffer for each vertex, using
            // clockwise winding
            Indexes[0] = 0;
            Indexes[1] = 1;
            Indexes[2] = 2;
            Indexes[3] = 2;
            Indexes[4] = 1;
            Indexes[5] = 3;
        }
    }


    /// <summary>
    /// Displays a screen aligned quad.
    /// </summary>
    public class ScreenAlignedQuad
    {
        private static Quad quad = null;
        //private static VertexDeclaration vertexDeclaration = null; //TODO: Initialize
        private Effect effect;

        /// <summary>
        /// Initializes a new instance of the <see cref="ScreenAlignedQuad"/> class.
        /// </summary>
        /// <param name="effect">Effect to use</param>
        /// <param name="graphics">The XNA graphics device.</param>
        public ScreenAlignedQuad(Effect effect, GraphicsDevice graphics)
        {
            if (quad == null)
            {
                quad = new Quad(new Vector3(0, 0, -1.0f), Vector3.Backward, Vector3.Up, 2f, 2f, graphics);
            }
            this.effect = effect;
        }

        /// <summary>
        /// Renders screen aligned quad.
        /// </summary>
        /// <param name="graphics">The XNA graphics device.</param>
        public void Draw(GraphicsDevice graphics)
        {
            graphics.VertexDeclaration = Quad.VertexDeclaration;
            effect.CommitChanges();
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                graphics.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
                    PrimitiveType.TriangleList, quad.Vertices, 0, 4, quad.Indexes, 0, 2);
                pass.End();
            }
            effect.End();
        }
    }
}
